package mmpathgen.callgraphanalysis;

import java.io.File;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import mmpathgen.util.ClassLoader;
import soot.Local;
import soot.PointsToSet;
import soot.Scene;
import soot.SootClass;
import soot.SootField;
import soot.SootMethod;
import soot.Unit;
import soot.Value;
import soot.ValueBox;
import soot.jimple.JimpleBody;
import soot.jimple.Stmt;
import soot.jimple.paddle.PaddleTransformer;
import soot.jimple.toolkits.callgraph.CallGraph;
import soot.options.Options;
import soot.options.PaddleOptions;
import soot.tagkit.LineNumberTag;
import soot.tagkit.Tag;

public class CallGraphGenByContextPTA {

	// args[0]=MainClass, args[1]=LocalMethod
	public static void main(String[] args) {

		// get the class names automatically
		File path = new File("D:\\workspace\\MMPath\\basic");

		// set main application class
		String mainClassName = "basic.HelloWorld";

		CallGraph cg = cgGen(path, mainClassName);

		UnfilteredCallGraph ucg = CallGraphAnalysis.getUnfilteredCallGraph(cg);
		// paddle4mmpath.printPossibleCallees(cg, src);
		CallGraphAnalysis.printCallGraphAsDot(ucg, mainClassName,
				"Paddle", "Filted");
	}

	public static CallGraph cgGen(File path, String mainClassName) {
		Options.v().set_whole_program(true);
		Options.v().set_keep_line_number(true);
		// Options.v().setPhaseOption("cg", "verbose:true");
		Options.v().setPhaseOption("cg", "safe-forname:true");
		Options.v().setPhaseOption("cg", "safe-newinstance:true");

		ClassLoader.loadClass(path, mainClassName);

		setPaddlePointsToAnalysis();

		CallGraph cg = Scene.v().getCallGraph();

		return cg;
	}

	public static CallGraph cgGen(String[] pkgList, String[][] clsList,
			String mainClassName) {

		Options.v().set_whole_program(true);
		Options.v().set_keep_line_number(true);
		// Options.v().setPhaseOption("cg", "verbose:true");
		Options.v().setPhaseOption("cg", "safe-forname:true");
		Options.v().setPhaseOption("cg", "safe-newinstance:true");

		ClassLoader.loadClass(pkgList, clsList, mainClassName);

		setPaddlePointsToAnalysis();

		CallGraph cg = Scene.v().getCallGraph();

		return cg;
	}

	@SuppressWarnings("unchecked")
	private static void setPaddlePointsToAnalysis() {
		System.out.println("[paddle] Starting analysis ...");

		System.err.println("Soot version string: "
				+ soot.Main.v().versionString);

		System.out.println("[paddle] Starting analysis ...");

		System.err.println("Soot version string: "+soot.Main.v().versionString);

		HashMap opt = new HashMap();
		opt.put("enabled","true");
		opt.put("verbose","true");
		opt.put("bdd","true");
		opt.put("backend","buddy");
		opt.put("context","kcfa");
		opt.put("k","2");
		//		opt.put("context-heap","true");
		opt.put("propagator","auto");
		opt.put("conf","ofcg");
		opt.put("order","32");
		opt.put("q","auto");
		opt.put("set-impl","double");
		opt.put("double-set-old","hybrid");
		opt.put("double-set-new","hybrid");
		opt.put("pre-jimplify","false");

		
		PaddleTransformer pt = new PaddleTransformer();
		PaddleOptions paddle_opt = new PaddleOptions(opt);
		pt.setup(paddle_opt);
		pt.solve(paddle_opt);
		soot.jimple.paddle.Results.v().makeStandardSootResults();	
		
		System.out.println("[paddle] Done!");

	}

	private static int getLineNumber(Stmt s) {
		Iterator<Tag> ti = s.getTags().iterator();
		while (ti.hasNext()) {
			Object o = ti.next();
			if (o instanceof LineNumberTag)
				return Integer.parseInt(o.toString());
		}
		return -1;
	}

	private static SootField getField(String classname, String fieldname) {
		Collection<SootClass> app = Scene.v().getApplicationClasses();
		Iterator<SootClass> ci = app.iterator();
		while (ci.hasNext()) {
			SootClass sc = ci.next();
			if (sc.getName().equals(classname))
				return sc.getFieldByName(fieldname);
		}
		throw new RuntimeException("Field " + fieldname
				+ " was not found in class " + classname);
	}

	private static Map/* <Integer,Local> */<Integer, Value> getLocals(
			SootClass sc, String methodname, String typename) {
		Map<Integer, Value> res = new HashMap<Integer, Value>();
		Iterator<SootMethod> mi = sc.getMethods().iterator();
		while (mi.hasNext()) {
			SootMethod sm = mi.next();
			System.err.println(sm.getName());
			if (true && sm.getName().equals(methodname) && sm.isConcrete()) {
				JimpleBody jb = (JimpleBody) sm.retrieveActiveBody();
				Iterator<Unit> ui = jb.getUnits().iterator();
				while (ui.hasNext()) {
					Stmt s = (Stmt) ui.next();
					int line = getLineNumber(s);
					// find definitions
					Iterator<ValueBox> bi = s.getDefBoxes().iterator();
					while (bi.hasNext()) {
						Object o = bi.next();
						if (o instanceof ValueBox) {
							Value v = ((ValueBox) o).getValue();
							if (v.getType().toString().equals(typename)
									&& v instanceof Local)
								res.put(new Integer(line), v);
						}
					}
				}
			}
		}

		return res;
	}

	private static void printLocalIntersects(
			Map/* <Integer,Local> */<Integer, Value> ls) {
		soot.PointsToAnalysis pta = Scene.v().getPointsToAnalysis();
		Iterator<Entry<Integer, Value>> i1 = ls.entrySet().iterator();
		while (i1.hasNext()) {
			Map.Entry<Integer, Value> e1 = i1.next();
			int p1 = ((Integer) e1.getKey()).intValue();
			Local l1 = (Local) e1.getValue();
			PointsToSet r1 = pta.reachingObjects(l1);
			Iterator<Entry<Integer, Value>> i2 = ls.entrySet().iterator();
			while (i2.hasNext()) {
				Map.Entry<Integer, Value> e2 = (Map.Entry<Integer, Value>) i2
						.next();
				int p2 = ((Integer) e2.getKey()).intValue();
				Local l2 = (Local) e2.getValue();
				PointsToSet r2 = pta.reachingObjects(l2);
				if (p1 <= p2)
					System.out.println("[" + p1 + "," + p2
							+ "]\t Container intersect? "
							+ r1.hasNonEmptyIntersection(r2));
			}
		}
	}

	private static void printFieldIntersects(
			Map/* <Integer,Local> */<Integer, Value> ls, SootField f) {
		soot.PointsToAnalysis pta = Scene.v().getPointsToAnalysis();
		Iterator<Entry<Integer, Value>> i1 = ls.entrySet().iterator();
		while (i1.hasNext()) {
			Map.Entry<Integer, Value> e1 = i1.next();
			int p1 = ((Integer) e1.getKey()).intValue();
			Local l1 = (Local) e1.getValue();
			PointsToSet r1 = pta.reachingObjects(l1, f);
			Iterator<Entry<Integer, Value>> i2 = ls.entrySet().iterator();
			while (i2.hasNext()) {
				Map.Entry<Integer, Value> e2 = (Map.Entry<Integer, Value>) i2
						.next();
				int p2 = ((Integer) e2.getKey()).intValue();
				Local l2 = (Local) e2.getValue();
				PointsToSet r2 = pta.reachingObjects(l2, f);
				if (p1 <= p2)
					System.out.println("[" + p1 + "," + p2
							+ "]\t Container.item intersect? "
							+ r1.hasNonEmptyIntersection(r2));
			}
		}
	}
}
